package com.company.pangu.aliyun.oss.client;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.company.nuwa.common.utils.JsonUtils;
import com.company.pangu.aliyun.oss.config.OssConfig;
import com.company.pangu.aliyun.oss.dto.Policy;
import com.company.pangu.aliyun.oss.request.OssTokenRequest;
import com.company.pangu.aliyun.oss.response.OssTokenResponse;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.net.URL;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Objects;

/**
 * <p>oss client</p >
 *
 * @author youyoukele
 * @version 1.0
 * @date 2021/8/31
 */
@Slf4j
public class OssClient {

    private final OssConfig ossConfig;

    private final OSS oss;

    private final IAcsClient ossAcsClient;

    public OssClient(OssConfig ossConfig) {
        this.ossConfig = ossConfig;

        this.oss = new OSSClientBuilder().build(ossConfig.getEndPoint(),
                ossConfig.getAccessKeyId(),
                ossConfig.getAccessKeySecret());

        String stsEndPoint = "sts." + ossConfig.getRegionId() + ".aliyuncs.com";
        DefaultProfile.addEndpoint(ossConfig.getRegionId(), "sts", stsEndPoint);
        IClientProfile profile = DefaultProfile.getProfile(
                ossConfig.getRegionId(),
                ossConfig.getAccessKeyId(),
                ossConfig.getAccessKeySecret()
        );
        this.ossAcsClient = new DefaultAcsClient(profile);
    }

    /**
     * shut down
     */
    public void shutdown() {
        if (Objects.nonNull(oss)) {
            this.oss.shutdown();
        }
        if (Objects.nonNull(ossAcsClient)) {
            this.ossAcsClient.shutdown();
        }
    }

    /**
     * 获取阿里云oss路径前缀
     *
     * @param useCdn 如果存在cdn地址， 是否使用cdn路径
     * @return
     */
    public String getOssPrefix(boolean useCdn) {
        if (useCdn) {
            return StringUtils.isNotBlank(ossConfig.getCdnAddr()) ? ossConfig.getCdnAddr() : ossConfig.getOssPrefix();
        }
        return ossConfig.getOssPrefix();
    }

    /**
     * 获取 oss token
     * <p>
     * https://help.aliyun.com/document_detail/100624.html?spm=5176.13910061.sslink.1.17134c67YQlXaj
     *
     * @param request
     * @return
     */
    public OssTokenResponse getOssToken(OssTokenRequest request) {
        try {
            String path = getPath(request.getPlatform(), request.getIdentity());
            AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest();
            assumeRoleRequest.setSysMethod(MethodType.POST);
            assumeRoleRequest.setRoleArn(ossConfig.getRoleArn());
            assumeRoleRequest.setRoleSessionName(ossConfig.getRoleSessionName());
            String bucketName = ossConfig.getBucketName();
            assumeRoleRequest.setPolicy(getPolicy(path, bucketName));
            AssumeRoleResponse acsResponse = ossAcsClient.getAcsResponse(assumeRoleRequest);

            OssTokenResponse response = OssTokenResponse.builder()
                    .accessKeyId(acsResponse.getCredentials().getAccessKeyId())
                    .accessKeySecret(acsResponse.getCredentials().getAccessKeySecret())
                    .securityToken(acsResponse.getCredentials().getSecurityToken())
                    .endPoint(ossConfig.getEndPoint())
                    .bucketName(bucketName)
                    .filePrefix(path)
                    .ossPrefix(ossConfig.getOssPrefix())
                    .build();

            log.info("oss get token request={}, response={}", request, response);
            return response;
        } catch (ClientException e) {
            log.error("invoke ali oss has error code:{}, message:{},", e.getErrCode(), e.getErrMsg());
            throw new RuntimeException("invoke ali oss error");
        }
    }

    /**
     * put object
     *
     * @param objectNameKey
     * @param file
     */
    public PutObjectResult putObject(String objectNameKey, File file) {
        PutObjectRequest putObjectRequest = new PutObjectRequest(ossConfig.getBucketName(), objectNameKey, file);
        // 设置该属性可以返回response。如果不设置，则返回的response为空。
        putObjectRequest.setProcess("true");
        return oss.putObject(putObjectRequest);
    }

    /**
     * put object
     *
     * @param bucketName
     * @param objectNameKey
     * @param file
     */
    public PutObjectResult putObject(String bucketName, String objectNameKey, File file) {
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectNameKey, file);
        // 设置该属性可以返回response。如果不设置，则返回的response为空。
        putObjectRequest.setProcess("true");
        return oss.putObject(putObjectRequest);
    }

    /**
     * delete object
     *
     * @param objectKey
     */
    public void deleteObject(String objectKey) {
        oss.deleteObject(ossConfig.getBucketName(), objectKey);
    }

    /**
     * delete object
     *
     * @param bucketName
     * @param objectKey
     */
    public void deleteObject(String bucketName, String objectKey) {
        oss.deleteObject(bucketName, objectKey);
    }

    /**
     * 为私有资源生成url
     *
     * <p>
     * {@code objectKey} 为 "risk-control/video/block/vi6%40qCj%40Bd4uZ6jkwlpmKnGB-1ufucd/frames/00_00_00.jpg"
     *
     * @param objectKey
     * @return
     */
    public String generatePresignedUrl(String objectKey) {
        // 设置URL过期时间为1小时。
        Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
        // 生成以GET方法访问的签名URL，访客可以直接通过浏览器访问相关内容。
        URL url = oss.generatePresignedUrl(ossConfig.getBucketName(), objectKey, expiration);
        return url.toString();
    }

    /**
     * oss授权规则
     *
     * @param path
     * @param bucketName
     * @return
     */
    private String getPolicy(String path, String bucketName) {
        Policy.StatementBean statementBean = Policy.StatementBean.builder()
                .effect("Allow")
                .action(Lists.newArrayList("oss:GetObject", "oss:PutObject", "oss:HeadObject"))
                .resource(Lists.newArrayList("acs:oss:*:*:" + bucketName + "/" + path + "*"))
                .build();

        Policy policy = Policy.builder()
                .statement(Lists.newArrayList(statementBean))
                .version("1")
                .bucket(bucketName)
                .path(path)
                .build();

        String result = JsonUtils.objectToJson(policy);

        log.info("ali oss policy:{}", result);
        return result;
    }

    /**
     * 获取存储path
     *
     * @param platform
     * @param identity
     * @return
     */
    private String getPath(String platform, String identity) {
        String currentDay = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        return MessageFormat.format("{0}/{1}/{2}", platform, currentDay, identity);
    }
}
